﻿
#region 作者和版权
/*************************************************************************************
* CLR 版本: 4.0.30319.42000
* 类 名 称: TcpClient
* 机器名称: SUNTIA-QL
* 命名空间: ServerFramework.Net.Tcp
* 文 件 名: TcpClient
* 创建时间: 2015/9/23 15:26:33
* 作 者: 乔龙 Qiaolong
* 版 权: TcpClient说明：本代码版权归乔龙所有，使用时必须带上乔龙网站地址 All Rights Reserved (C) 2015 - 2013
* 签 名: To be or not, it is not a problem !
* 网 站: http://www.suntia.net
* 邮 箱: qiaolong306@qq.com 
* 唯一标识： 2806fbc4-ff09-4950-8b16-ac8d1c519cc6 
*
* 登录用户: simida
* 所 属 域: SUNTIA-QL

* 创建年份: 2015
* 修改时间:
* 修 改 人:
* 
************************************************************************************/
#endregion

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;
using ServerFramework.Net.Msg;
using ServerFramework.Tools;


namespace ServerFramework.Net.Tcp
{
    /// <summary>
    /// tcp 网络客户端
    /// </summary>
    public class TcpClient:ISender
    {
        public Action<string> onClientClose;             //当用户关闭
        public Action<MsgPacket> onRecvMessage;     //当收到消息
        private System.Timers.Timer _timer;         //发送心跳包的定时器

        private byte[] _heartMsg;

        /// <summary>
        /// 用户连接
        /// </summary>
        private Socket _conn;
        /// <summary>
        /// 读取缓存
        /// </summary>
        private byte[] _readBuffer = new byte[1024];
        /// <summary>
        /// 读取缓存
        /// </summary>
        private List<byte> _readCache = new List<byte>();
        /// <summary>
        /// 是否正在读取
        /// </summary>
        private bool _isReading = false;

        public TcpClient()
        {
            try
            {
                //创建客户端连接对象
                _conn = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
                //20秒发送一次心跳包
                _timer = new System.Timers.Timer(20000);
                _timer.Elapsed += _timer_Elapsed;
                _heartMsg = GetHeartMsg();
            }
            catch (Exception e) 
            {
                Log.WriteLog(ELogLevel.LOG_ERROR, e.Message);
            }
        }


        /// <summary>
        /// 获取心跳包
        /// </summary>
        /// <returns></returns>
        byte[] GetHeartMsg()
        {
            MsgBase msg = new MsgBase();
            msg._msgId = SystemMsgID.heartId;
            return msg.Pack();
        }

        void _timer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
        {
            if (_conn.Connected)
            {
                Send(_heartMsg);
            }
        }
        /// <summary>
        /// 连接到服务器
        /// </summary>
        /// <param name="ip"></param>
        /// <param name="port"></param>
        /// <returns></returns>
        public bool Connect(string ip, int port)
        {
            //连接到服务器
            try
            {
                _conn.Connect(ip, port);
                //开启异步消息接收 消息到达后会直接写入 缓冲区 readbuff
                _conn.BeginReceive(_readBuffer, 0, _readBuffer.Length, SocketFlags.None, ReceiveCallBack, null);
                //开启发送心跳包
                _timer.Start();
                return true;
            }
            catch (Exception e)
            {
                Log.WriteLog(ELogLevel.LOG_ERROR, e.Message);
                return false;
            }
        }

        private void ReceiveCallBack(IAsyncResult ar)
        {
            try
            {
                //获取当前收到的消息长度()
                int length = _conn.EndReceive(ar);
                ///收到消息长度为0 说明与服务器断开连接
                if (length == 0)
                {
                    Close("远程服务器主动断开连接");
                    return;
                }
                byte[] message = new byte[length];
                Buffer.BlockCopy(_readBuffer, 0, message, 0, length);
                _readCache.AddRange(message);
                if (!_isReading)
                {
                    _isReading = true;
                    onData();
                }
                //尾递归 再次开启异步消息接收 消息到达后会直接写入 缓冲区 readbuff
                _conn.BeginReceive(_readBuffer, 0, _readBuffer.Length, SocketFlags.None, ReceiveCallBack, null);
            }
            catch (Exception)
            {
                Close("远程服务器主动断开连接");
            }
        }

        //缓存中有数据处理
        void onData()
        {
            //解码消息存储对象
            byte[] buff = null;
            //当粘包解码器存在的时候 进行粘包处理
            buff = MsgPacket.SplitPacket(ref _readCache);
            //消息未接收全 退出数据处理 等待下次消息到达
            if (buff == null) { _isReading = false; return; }
            MsgPacket msg = MsgPacket.SplitPacket(this, buff, 0, buff.Length);
            //通知应用层 有消息到达
            try
            {
                onRecvMessage(msg);
            }
            catch (Exception e)
            {
                Log.WriteLog(ELogLevel.LOG_ERROR, e);
            }
            //尾递归 防止在消息处理过程中 有其他消息到达而没有经过处理
            onData();
        }


        public void Send(byte[] data)
        {
            try
            {
                if (_conn == null)
                {
                    return;
                }
                _conn.Send(data);
            }
            catch (Exception e)
            {
                Log.WriteLog(ELogLevel.LOG_ERROR, e.Message);
            }
        }

        /// <summary>
        /// 关闭客户端
        /// </summary>
        /// <param name="errorMessage"></param>
        public void Close(string errorMessage)
        {
            try
            {
                if (_conn != null)
                {
                    _timer.Stop();
                    _conn.Shutdown(SocketShutdown.Both);
                    _conn.Close();
                    _readCache.Clear();
                    _conn = null;
                    if (onClientClose != null)
                    {
                        onClientClose(errorMessage);
                    }
                    Log.WriteLog(ELogLevel.LOG_INFO,string.Format("与服务器断开连接[{0}]",errorMessage));
                }
            }
            catch (Exception e)
            {
                Log.WriteLog(ELogLevel.LOG_ERROR, e.Message);
            }
        }
    }
}


